using System;
using System.Runtime.CompilerServices;

namespace Letter.IO.Binary;

public ref struct SpanReader
{
    internal SpanReader(ReadOnlySpan<byte> buf)
    {
        this.index = 0;
        this.buf = buf;
    }

    private int index;
    private ReadOnlySpan<byte> buf;

    public int Length
    {
        get { return this.buf.Length; }
    }

    public int ReadableLength
    {
        get { return this.Length - this.index; }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool ReadBoolean() => this.ReadBuffer(1).FReadBoolean();


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public sbyte ReadInt8() => this.ReadBuffer(sizeof(sbyte)).FReadInt8();


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public byte ReadUInt8() => this.ReadBuffer(sizeof(byte)).FReadUInt8();


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public char ReadChar() => this.ReadBuffer(sizeof(char)).FReadChar();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public char ReadCharLE() => this.ReadBuffer(sizeof(char)).FReadCharLE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public char ReadCharBE() => this.ReadBuffer(sizeof(char)).FReadCharBE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ReadString()
    {
        var length = this.ReadInt32BE();
        char[] chars = new char[length];
        for (int i = 0; i < length; i++)
        {
            chars[i] = this.ReadChar();
        }

        return new string(chars);
    }


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public short ReadInt16LE() => this.ReadBuffer(sizeof(short)).FReadInt16LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public short ReadInt16BE() => this.ReadBuffer(sizeof(short)).FReadInt16BE();
    

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ushort ReadUInt16LE() => this.ReadBuffer(sizeof(ushort)).FReadUInt16LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ushort ReadUInt16BE() => this.ReadBuffer(sizeof(ushort)).FReadUInt16BE();
    
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int ReadInt32LE() => this.ReadBuffer(sizeof(int)).FReadInt32LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int ReadInt32BE() => this.ReadBuffer(sizeof(int)).FReadInt32BE();
    
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public uint ReadUInt32LE() => this.ReadBuffer(sizeof(uint)).FReadUInt32LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public uint ReadUInt32BE() => this.ReadBuffer(sizeof(uint)).FReadUInt32BE();
    
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long ReadInt64LE() => this.ReadBuffer(sizeof(long)).FReadInt64LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long ReadInt64BE() => this.ReadBuffer(sizeof(long)).FReadInt64BE();

    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ulong ReadUInt64LE() => this.ReadBuffer(sizeof(ulong)).FReadUInt64LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ulong ReadUInt64BE() => this.ReadBuffer(sizeof(ulong)).FReadUInt64BE();
    
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public float ReadFloat32LE() => this.ReadBuffer(sizeof(float)).FReadFloat32LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public float ReadFloat32BE() => this.ReadBuffer(sizeof(float)).FReadFloat32BE();
    
    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public double ReadFloat64LE() => this.ReadBuffer(sizeof(double)).FReadFloat64LE();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public double ReadFloat64BE() => this.ReadBuffer(sizeof(double)).FReadFloat64BE();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ReadOnlySpan<byte> ReadBuffer(int size)
    {
        var span = this.buf.Slice(index, size);
        index += size;
        return span;
    }

}